VSUDP通信

您所在的位置:网站首页 vs udp编程 VSUDP通信

VSUDP通信

#VSUDP通信| 来源: 网络整理| 查看: 265

好久没弄VS的UDP通信了,使用方式都忘记的差不多了。今天遇到了正好学习一下。

UDP理论:

UDP参考链接:

socket函数

为了执行网络输入输出,一个进程必须做的第一件事就是调用socket函数获得一个文件描述符。

#include int socket(int family,int type,int protocol);         返回:非负描述字---成功   -1---失败 第一个参数指明了协议簇,目前支持5种协议簇,最常用的有AF_INET(IPv4协议)和AF_INET6(IPv6协议);第二个参数指明套接口类型,有三种类型可选:SOCK_STREAM(字节流套接口)、SOCK_DGRAM(数据报套接口)和SOCK_RAW(原始套接口);如果套接口类型不是原始套接口,那么第三个参数就为0。 bind函数

为套接口分配一个本地IP和协议端口,对于网际协议,协议地址是32位IPv4地址或128位IPv6地址与16位的TCP或UDP端口号的组合;如指定端口为0,调用bind时内核将选择一个临时端口,如果指定一个通配IP地址,则要等到建立连接后内核才选择一个本地IP地址。

#include   int bind(int sockfd, const struct sockaddr * server, socklen_t addrlen); 返回:0---成功   -1---失败  第一个参数是socket函数返回的套接口描述字;第二和第第三个参数分别是一个指向特定于协议的地址结构的指针和该地址结构的长度。 recvfrom函数

UDP使用recvfrom()函数接收数据,他类似于标准的read(),但是在recvfrom()函数中要指明目的地址。

#include   #include   ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr * from, size_t *addrlen); 返回接收到数据的长度---成功   -1---失败  前三个参数等同于函数read()的前三个参数flags参数是传输控制标志。最后两个参数类似于accept的最后两个参数。 sendto函数

UDP使用sendto()函数发送数据,他类似于标准的write(),但是在sendto()函数中要指明目的地址。

#include   #include   ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr * to, int addrlen); 返回发送数据的长度---成功   -1---失败  前三个参数等同于函数read()的前三个参数flags参数是传输控制标志。参数to指明数据将发往的协议地址他的大小由addrlen参数来指定 文章转载:(博主写的已经很详细了,为了防止丢失在这边重新保存一遍)

UDP通信模型: 在这里插入图片描述

vs 实现udp通信 服务器端编程的步骤 创建套接字(socket)将套接字和IP地址、端口号绑定在一起(bind)等待客户端发起数据通信(recvfrom/recvto)关闭套接字 客户端编程的步骤 创建套接字(socket)向服务器发起通信(recvfrom/recvto)关闭套接字 知识点: 在vs中一般使用Winsock2实现网络通信功能,所以需要引进头文件winsock2.h和库文件"ws2_32.lib"。

WinSock2 是连接系统和用户使用的软件之间用于交流的一个接口,这个功能就是修复软件与系统正确的通讯的作用。

Winsock2 SPI(Service Provider Interface)服务提供者接口建立在Windows开放系统架构WOSA(Windows Open System Architecture)之上,是Winsock系统组件提供的面向系统底层的编程接口。

Winsock系统组件向上面向用户应用程序提供一个标准的API接口;向下在Winsock组件和Winsock服务提供者(比如TCP/IP协议栈)之间提供一个标准的SPI接口。

各种服务提供者是Windows支持的DLL,挂载在Winsock2 的Ws2_32.dll模块下。

对用户应用程序使用的Winsock2 API中定义的许多内部函数来说,这些服务提供者都提供了它们的对应的运作方式(例如API函数WSAConnect有相应的SPI函数WSPConnect)。

多数情况下,一个应用程序在调用Winsock2 API函数时,Ws2_32.dll会调用相应的Winsock2 SPI函数,利用特定的服务提供者执行所请求的服务。

pragma comment(lib,“Ws2_32.lib”) 作用: 表示链接Ws2_32.lib这个库,和在工程设置里写上链入Ws2_32.lib的效果一样,不过这种方法写的程序别人在使用你的代码的时候就不用再设置工程settings了。告诉连接器连接的时候要找ws2_32.lib,这样你就不用在linker的lib设置里指定这个lib了。ws2_32.lib是winsock2的库文件。WinSock2就相当于连接系统和你使用的软件之间交流的一个接口,可能这个功能就是修复软件与系统正确的通讯的作用。 WASDATA 一种数据结构。这个结构被用来存储被WSAStartup函数调用后返回的Windows Sockets数据。它包含Winsock.dll执行的数据。 结构原型: typedef struct WSAData { WORD wVersion; WORD wHighVersion; #ifdef _WIN64 unsigned short iMaxSockets; unsigned short iMaxUdpDg; char FAR * lpVendorInfo; char szDescription[WSADESCRIPTION_LEN+1]; char szSystemStatus[WSASYS_STATUS_LEN+1]; #else char szDescription[WSADESCRIPTION_LEN+1]; char szSystemStatus[WSASYS_STATUS_LEN+1]; unsigned short iMaxSockets; unsigned short iMaxUdpDg; char FAR * lpVendorInfo; #endif } WSADATA, FAR * LPWSADATA; MAKEWORD(a, b):声明调用不同的Winsock版本 makeword是将两个byte型合并成一个word型,一个在高8位(b),一个在低8位(a)返回值:一个无符号16位整形数。MAKEWORD(1,1)和MAKEWORD(2,2)的区别在于:前者只能一次接收一次,不能马上发送,而后者能。MAKEWORD(2,2)就是调用2.2版,MAKEWORD(1,1)就是调用1.1版。1.1版只支持TCP/IP协议,而2.0版可以支持多协议。2.0版有良好的向后兼容性,任何使用1.1版的源代码、二进制文件、应用程序都可以不加修改地在2.0规范下使用。 WSAStartup(sockVersion, &wsadata) WSAStartup,即WSA(Windows Sockets Asynchronous,Windows异步套接字)的启动命令。是Windows下的网络编程接口软件Winsock1 或 Winsock2 里面的一个命令。WSAStartup必须是应用程序或DLL调用的第一个Windows Sockets函数。它允许应用程序或DLL指明Windows Sockets API的版本号及获得特定Windows Sockets实现的细节。应用程序或DLL只能在一次成功的WSAStartup()调用之后才能调用进一步的Windows Sockets API函数。 sockaddr结构 truct sockaddr { unsigned short sa_family; /*addressfamily,AF_xxx*/ char sa_data[14]; /*14bytesofprotocoladdress*/ } ; sa_family: 是地址家族,一般都是“AF_xxx”的形式。通常大多用的是都是AF_INET,代表TCP/IP协议族。 sa_data: 是14字节协议地址。 此数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息。但一般编程中并不直接针对此数据结构操作,而是使用另一个与sockaddr等价的数据结构(在WinSock2.h中定义) struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; sin_family指代协议族,在socket编程中只能是AF_INET sin_port存储端口号(使用网络字节顺序),在linux下,端口号的范围0~65535,同时0~1024范围的端口号已经被系统使用或保留。 sin_addr存储IP地址,使用in_addr这个数据结构 sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。 sockaddr_in和sockaddr是并列的结构,指向sockaddr_in的结构体的指针也可以指向sockaddr的结构体,并代替它。也就是说,你可以使用sockaddr_in建立你所需要的信息,然后用memset函数初始化:memset((char)&mysock,0,sizeof(mysock)) c_str c_str是Borland封装的String类中的一个函数,它返回当前字符串的首字符地址。当需要打开一个由用户自己输入文件名的文件时,可以这样写:ifstream in(st.c_str())。c_str()返回的是一个分配给const char的地址,其内容已设定为不可变更,如果再把此地址赋给一个可以变更内容的char变量,就会产生冲突。但是如果放入函数调用,或者直接输出,因为这些函数和输出都是把字符串指针作为 const char*引用的,所以不会有问题。c_str() 以const char* 类型返回 string 内含的字符串。如果一个函数要求char*参数,可以使用c_str()方法: string s = "Hello World!"; printf("%s", s.c_str()); //输出 "Hello World!" c_str在打开文件时的用处: 当需要打开一个由用户自己输入文件名的文件时,可以这样写:ifstream in(st.c_str());。其中st是string类型,存放的即为用户输入的文件名。 memset void *memset(void *s, int ch, size_t n);将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法 。 recvfrom recvfrom( _In_ SOCKET s, _Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR * buf, //接收数据的缓冲区 _In_ int len, //缓冲区的大小 _In_ int flags, //标志位,调用操作方式 _Out_writes_bytes_to_opt_(*fromlen, *fromlen) struct sockaddr FAR * from, //sockaddr结构地址 _Inout_opt_ int FAR * fromlen //sockaddr结构大小地址 ); sendto WSAAPI sendto( _In_ SOCKET s, //socket _In_reads_bytes_(len) const char FAR * buf, //发送数据的缓冲区 _In_ int len, //缓冲区大小 _In_ int flags, //标志位,调用操作方式 _In_reads_bytes_(tolen) const struct sockaddr FAR * to, //sockaddr结构地址 _In_ int tolen //sockaddr结构大小地址 ); 示例代码 服务端 // server.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include "pch.h" #include #include #include #include #pragma comment(lib,"ws2_32.lib") using namespace std; int main() { //设置版本号 WORD sockVersion = MAKEWORD(2, 2); //定义一个WSADATA类型的结构体,存储被WSAStartup函数调用后返回的Windows Sockets数据 WSADATA wsadata; //初始化套接字,启动构建,将“ws2_32.lib”加载到内存中 if (WSAStartup(sockVersion, &wsadata)) { printf("WSAStartup failed \n"); return 0; } //创建一个套接字,即创建一个内核对象 SOCKET hServer = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (hServer == INVALID_SOCKET) { printf("socket failed \n"); return 0; } //创建服务器端地址并绑定端口号的IP地址 sockaddr_in addrServer; addrServer.sin_family = AF_INET; addrServer.sin_port = htons(8889); addrServer.sin_addr.S_un.S_addr = INADDR_ANY; // 初始化内核对象,传参给内核对象,此时数据可能都处于未就绪链表 int nRet = bind(hServer, (sockaddr*)&addrServer, sizeof(addrServer)); if (nRet == SOCKET_ERROR) { printf("socket bind failed\n"); closesocket(hServer); WSACleanup(); return 0; } //创建一个客服端地址 sockaddr_in addrClient; int nlen = sizeof(addrClient); //创建一个中间变量,用于存放用户输入的信息 //string str; //用于接受数据的缓冲区。 char buffer[1024]; //初始化缓冲区 memset(buffer, 0, sizeof(buffer)); int irecv; int isend; //可以循环接受数据 while (true) { //接收数据: //接受客户端的消息 irecv = recvfrom(hServer, buffer, sizeof(buffer), 0, (SOCKADDR*)&addrClient, &nlen); //缓冲区有数据,开始读取数据 if (irecv > 0) { //判断数据是否为结束标志,若是则关闭服务器 if (! (strcmp(buffer,"byebye"))) { //关闭服务器套接字 cout cout //套接字信息结构 WSADATA wsadata; //设置版本号 WORD sockVersion = MAKEWORD(2, 2); //建立一个客户端套接字; SOCKET sClient; //启动构建,将“为ws2_32.lib”加载到内存中,做一些初始化工作 if (WSAStartup(sockVersion, &wsadata) != 0) { //判断是否构建成功,若失败,则客户端打印一句提示话。 printf("WSAStartup failed \n"); return 0; } //创建客户端udp套接字 sClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (SOCKET_ERROR == sClient) { printf("socket failed !\n"); return 0; } //创建服务器端地址 sockaddr_in serverAddr; //创建服务器端地址 sockaddr_in clientAddr; //设置服务器端地址,端口号,协议族 serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(8889); serverAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //获取服务器地址和客户端地址构造体的长度 int slen = sizeof(serverAddr); int clen = sizeof(clientAddr); //设置接受数据缓冲区大小 char buffer[1024]; memset(buffer, 0, sizeof(buffer)); //用于记录发送函数和接受函数的返回值 int iSend = 0; int iRcv = 0; //string str; cout cout cout cout


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3